﻿Imports System.Linq

Namespace Objects

    Public Class ProgressParts

        Private _delen As List(Of ProgressPart)

        Private _sw As Stopwatch

        Public Sub New(ByVal ParamArray gewichten() As Long)
            _delen = New List(Of ProgressPart)
            For Each gewicht As Long In gewichten
                _delen.Add(New ProgressPart(gewicht))
            Next
            _sw = New Stopwatch()
            _sw.Start()
        End Sub

        Public Sub SetGewicht(ByVal deel As Integer, ByVal gewicht As Long)
            _delen(deel).Gewicht = gewicht
        End Sub

        Public Sub Start(ByVal deel As Integer, ByVal todo As Long)
            Start(deel, todo, _sw.ElapsedTicks * 10000000 / Stopwatch.Frequency) ' = aantal eenheden van 100 ns
        End Sub

        Public Sub Start(ByVal deel As Integer, ByVal todo As Long, ByVal elapsedTicks As Double)
            _delen(deel).Start(todo, elapsedTicks)
        End Sub

        Public Sub Increase(ByVal deel As Integer)
            _delen(deel).Increase()
        End Sub

        Public Sub SetDoneValue(ByVal deel As Integer, ByVal doneValue As Integer)
            _delen(deel).Done = doneValue
        End Sub

        Public Sub Finish(ByVal deel As Integer)
            _delen(deel).Finish()
        End Sub

        Public Function GetProgressValueOp1000() As Integer
            Dim totaalGewicht As Long = _delen.Sum(Function(d) d.Gewicht)
            Return CInt(_delen.Sum(Function(d) d.GetProgressValue()) * 1000 / totaalGewicht)
        End Function

        Public Function GetTimeRemaining() As TimeSpan
            Dim elapsedTicks As Double = _sw.ElapsedTicks * 10000000 / Stopwatch.Frequency ' = aantal eenheden van 100 ns 
            Return GetTimeRemaining(elapsedTicks) ' apart om te unittesten
        End Function

        Public Function GetTimeRemaining(ByVal elapsedTicks As Double) As TimeSpan
            Dim gewichtDone As Long = _delen _
                .Where(Function(d) d.IsDone() OrElse d.IsBusy()) _
                .Sum(Function(d) d.GetProgressValue())
            Dim gewichtToDo As Long = _delen.Where(Function(d) Not d.IsDone() AndAlso Not d.IsBusy()) _
                .Sum(Function(d) d.Gewicht)
            Dim busyDeel As ProgressPart = _delen.FirstOrDefault(Function(d) d.IsBusy())
            Dim timeRemainingForBusyPart As New TimeSpan(0)
            If (busyDeel IsNot Nothing) Then
                timeRemainingForBusyPart = busyDeel.GetTimeRemaining(elapsedTicks)
            End If
            Dim timeRemainingForNotDoneParts As New TimeSpan(0) ' busy deel niet meegerekend
            If (gewichtDone > 0) Then
                timeRemainingForNotDoneParts = New TimeSpan(Convert.ToInt64(elapsedTicks * CDbl(gewichtToDo) / CDbl(gewichtDone)))
            End If
            Return timeRemainingForBusyPart + timeRemainingForNotDoneParts
        End Function

        Private Class ProgressPart

            Public ToDo As Long
            Public Done As Long
            Public Gewicht As Long
            Public ElapsedTicksAtStart As Double ' = aantal eenheden van 100 ns

            Public Sub New(ByVal gewicht As Long)
                ToDo = 100
                Done = 0
                Me.Gewicht = gewicht
            End Sub

            ''' <summary>
            ''' 
            ''' </summary>
            ''' <param name="todo"></param>
            ''' <param name="elapsedTicksAtStart"> = aantal eenheden van 100 ns</param>
            ''' <remarks></remarks>
            Public Sub Start(ByVal todo As Long, ByVal elapsedTicksAtStart As Double)
                Me.Done = 0
                Me.ToDo = todo
                Me.ElapsedTicksAtStart = elapsedTicksAtStart
            End Sub

            ''' <summary>
            ''' Deel dat gedaan is (tussen 0 en Gewicht).
            ''' </summary>
            ''' <returns></returns>
            ''' <remarks>Mag nooit groter worden dan Gewicht</remarks>
            Public Function GetProgressValue() As Integer
                If (ToDo > 0) Then
                    Return CInt(Done * Gewicht / ToDo)
                Else
                    ' als er niets te doen is => als afgewerkt geven
                    Return CInt(Gewicht)
                End If
            End Function

            Public Sub Finish()
                Done = ToDo
            End Sub

            Public Sub Increase()
                If (Done < ToDo) Then
                    Done += 1
                End If
            End Sub

            Public Function IsDone() As Boolean
                Return Done = ToDo
            End Function

            Public Function IsBusy() As Boolean
                Return Done > 0 AndAlso Done < ToDo
            End Function

            Public Function GetTimeRemaining(ByVal totalElapsedTicks As Double) As TimeSpan
                Dim elapsedTicks As Double = totalElapsedTicks - Me.ElapsedTicksAtStart
                If (Done = 0) Then
                    Return New TimeSpan(0)
                End If
                Dim geschatteTijd As New TimeSpan(Convert.ToInt64(elapsedTicks * CDbl(ToDo - Done) / CDbl(Done)))
                Return geschatteTijd
            End Function

        End Class

    End Class

    Public Class ProgressPartRunner
        Implements IDisposable

        Private _parts As ProgressParts
        Private _deel As Integer

        Public Sub New(ByVal parts As ProgressParts, ByVal deel As Integer, ByVal todo As Long)
            _parts = parts
            _deel = deel
            parts.Start(deel, todo)
        End Sub

        Public Sub Increase()
            _parts.Increase(_deel)
        End Sub

        Private disposedValue As Boolean = False        ' To detect redundant calls

        ' IDisposable
        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    _parts.Finish(_deel)
                End If
            End If
            Me.disposedValue = True
        End Sub

#Region " IDisposable Support "
        ' This code added by Visual Basic to correctly implement the disposable pattern.
        Public Sub Dispose() Implements IDisposable.Dispose
            ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub
#End Region

    End Class

End Namespace
